#include "rendercontext.h"

namespace raster {

using namespace mathengine;

void RenderContext::setModel(const mathengine::Matrix44f &m) {
    model_ = m;
}

// coordinate transformation from world space to eye/camera space
//
// https://www.cnblogs.com/fortunely/p/18709389
void RenderContext::lookAt(const mathengine::Vec3f &eye, const mathengine::Vec3f &center, const mathengine::Vec3f &up) {
    Vec3f z = (eye - center).normalize();
    Vec3f x = crossProduct(up, z).normalize();
    Vec3f y = crossProduct(z, x).normalize();

    Matrix44f Minv = Matrix44f::identity();
    Matrix44f Tr   = Matrix44f::identity();
    for (int i = 0; i < 3; i++) {
        Minv.setValue(0, i, x[i]);
        Minv.setValue(1, i, y[i]);
        Minv.setValue(2, i, z[i]);
        Tr.setValue(i, 3, -center[i]);
    }
    view_ = Minv * Tr;
}

// Projection=
// (1
//    1
//       1
//       r  1), r = -1/c, c is camera's z coordinate
//
// https://www.cnblogs.com/fortunely/p/18819642
void RenderContext::projection(float coeff) {
    projection_ = Matrix44f::identity();
    projection_.setValue(3, 2, coeff);
}

// Viewport=
// (w/2  0   0                         x+w/2
//  0    h/2 0                         y+h/2
//  0    0   (depth_far-depth_near)/2  (depth_far+depth_near)/2
//  0    0   0                         1     )
// depth_far = depth, depth_near = 0
//
// https://www.cnblogs.com/fortunely/p/18106741
void RenderContext::viewport(int x, int y, int w, int h, int depth) {
    float depth_far  = static_cast<float>(depth);
    float depth_near = 0;
    viewport_ = Matrix44f::identity();
    viewport_.setValue(0, 3, x + w / 2.f);
    viewport_.setValue(1, 3, y + h / 2.f);
    viewport_.setValue(2, 3, (depth_far+depth_near) / 2.f);
    viewport_.setValue(0, 0, w / 2.f);
    viewport_.setValue(1, 1, h / 2.f);
    viewport_.setValue(2, 2, (depth_far-depth_near) / 2.f);
}

Matrix44f RenderContext::getViewport() const {
    return viewport_;
}

Matrix44f RenderContext::getMVP() const {
    return projection_ * view_ * model_;
}

}
